package com.uw.scenic.generate;


import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import com.uw.scenic.MybatisFasterApplication;
import com.uw.scenic.helper.ClazzHelper;
import com.uw.scenic.utils.FileUtil;
import com.uw.scenic.utils.StrUtil;
import org.joda.time.DateTime;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.StringUtils;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;

import java.io.File;
import java.lang.reflect.Field;
import java.util.*;
import org.springframework.core.env.Environment;

/**
 * 生成器入口
 * @author  yls
 * @date 2021-11-05
 */
@RunWith(SpringRunner.class)
@SpringBootTest(classes={MybatisFasterApplication.class})// 指定启动类
public class FasterGenerator {

    /**
     * 环境配置
     */
    @Autowired
    private  Environment env;
    /**
     * 模板引擎
     */
    @Autowired
    private TemplateEngine templateEngine;


    //数据库类型
    private final DbType dbType = DbType.MYSQL;



    /**
     * 1、生成所有的mapper
     * @throws NoSuchFieldException
     * @throws IllegalAccessException
     */
    @Test
    public  void generateMapper() throws NoSuchFieldException, IllegalAccessException {
        //1、指定生成的表名
        String[] tableNames = new String[]{"user"};

        //读取当前项目所在父包
        String packageName= env.getProperty("com.cool.faster.modelParentPackage");
        //1、利用mybatisplus  生成mapper
        generateMapper(packageName,tableNames);
    }


    /**
     * 2、生成所有的controller+service
     */
    @Test
    public void generateAll(){
        //通过反射读取指定目录的所有的实体类
        String modelPackage= env.getProperty("com.cool.faster.modelPackage");

        Set<Class<?>> classSet=null;

        String proejctRootPath=env.getProperty("com.cool.faster.toGenerateProjectAbsPath");
        if(StringUtils.isEmpty(proejctRootPath)){
            proejctRootPath=System.getProperty("user.dir");
            //获取所有的model实体类
            classSet = ClazzHelper.getClasses(modelPackage);
        }else {
            String javaPath = proejctRootPath + "/src/main/java/";
            classSet=ClazzHelper.loadClassesByClzzLoader(proejctRootPath,javaPath,modelPackage);
        }



        Set<Class<?>> filterClassSet= new LinkedHashSet<Class<?>>();
        for(Class<?> clzzz:classSet){
            //过滤默认的父类
//            if(clzzz.isAssignableFrom(Entity.class)||
//                    clzzz.isAssignableFrom(SuperEntity.Update.class)||
//                    clzzz.isAssignableFrom(SuperEntity.Save.class)||
//                    clzzz.isAssignableFrom(SuperEntity.class)
//                    ||clzzz.isAssignableFrom(TreeEntity.class)){
//                continue;
//            }
            filterClassSet.add(clzzz);
        }
        //1、生成所有的controller类
        generateController(filterClassSet);
        //2、生成所有的service类
        generateService(filterClassSet);
        //3、生成所有的serviceImpl类
        generateServiceImpl(filterClassSet);
    }







    /**
     * 根据表自动生成
     * @param packageName 包名
     * @param tableNames 表名
     * @author Terry
     */
    @SuppressWarnings("unused")
    private void generateMapper(String packageName, String... tableNames) throws NoSuchFieldException, IllegalAccessException {
        generateMyabtisMapperByMybaitsPlus(true, packageName, tableNames);
    }


    /**
     * 根据表自动生成
     * @param serviceNameStartWithI 默认为false
     * @param packageName      包名
     * @param tableNames      表名
     * @author Terry
     */
    private void generateMyabtisMapperByMybaitsPlus(boolean serviceNameStartWithI, String packageName, String... tableNames) throws NoSuchFieldException, IllegalAccessException {
        //配置数据源
        DataSourceConfig dataSourceConfig = getDataSourceConfig();
        // 策略配置
        StrategyConfig strategyConfig = getStrategyConfig(tableNames);
        //全局变量配置
        GlobalConfig globalConfig = getGlobalConfig(serviceNameStartWithI);
        //包名配置
        PackageConfig packageConfig = getPackageConfig(packageName);
        //自动生成
        atuoGenerator(dataSourceConfig, strategyConfig, globalConfig, packageConfig);
    }

    /**
     * 1、配置数据源
     * @return 数据源配置 DataSourceConfig
     * @author Terry
     */
    private DataSourceConfig getDataSourceConfig() {
      //数据库连结信息
      String dbUrl = env.getProperty("com.cool.faster.datasource.dbUrl");
      String driver = env.getProperty("com.cool.faster.datasource.driver");
      String userName =env.getProperty("com.cool.faster.datasource.username");
      String password = env.getProperty("com.cool.faster.datasource.password");

        return new DataSourceConfig().setDbType(dbType)
                .setUrl(dbUrl)
                .setUsername(userName)
                .setPassword(password)
                .setDriverName(driver);
    }


    /**
     * 2、策略配置
     * @param tableNames 表名
     * @return StrategyConfig
     * @author Terry
     */
    private StrategyConfig getStrategyConfig(String... tableNames) {
        return new StrategyConfig()
                // 全局大写命名 ORACLE 注意
                .setCapitalMode(true)
                //从数据库表到文件的命名策略
                .setNaming(NamingStrategy.underline_to_camel)
                //需要生成的的表名，多个表名传数组
                .setInclude(tableNames)
                //公共父类
                //.setSuperControllerClass(superControllerClass)
                //.setSuperEntityClass(superEntityClass)
                // 写于父类中的公共字段
                //.setSuperEntityColumns("id")
                //使用lombok
                .setEntityLombokModel(true)
                //rest风格
                .setRestControllerStyle(true);
    }


    /**
     * 3、全局配置
     * @param serviceNameStartWithI false
     * @return GlobalConfig
     * @author Terry
     */
    private GlobalConfig getGlobalConfig(boolean serviceNameStartWithI) {
        String author=env.getProperty("com.cool.author");
        String proejctRootPath=env.getProperty("com.cool.faster.toGenerateProjectAbsPath");
        if(StringUtils.isEmpty(proejctRootPath)){
            proejctRootPath=System.getProperty("user.dir");
        }
        String javaPath=proejctRootPath+"/src/main/java/";
        GlobalConfig globalConfig = new GlobalConfig();
        globalConfig
                .setBaseColumnList(true)
                .setBaseResultMap(true)
                .setActiveRecord(false)
                //作者
                .setAuthor(author)
                //设置输出路径
                .setOutputDir(javaPath)
                .setFileOverride(true);
        if (!serviceNameStartWithI) {
            //设置service名
            globalConfig.setServiceName("%sService");
        }
        return globalConfig;
    }

    /**
     * 4、设置包名
     * @param packageName 父路径包名
     * @param packageName 模块名
     * @return PackageConfig 包名配置
     * @author Terry
     */
    private PackageConfig getPackageConfig(String packageName) {
        return new PackageConfig()
                .setParent(packageName)
                .setMapper("mapper")
                .setController("controller")
                .setEntity("entity");
    }


    /**
     * 5、集成
     * @param dataSourceConfig 配置数据源
     * @param strategyConfig  策略配置
     * @param config      全局变量配置
     * @param packageConfig  包名配置
     * @author Terry
     */
    private void atuoGenerator(DataSourceConfig dataSourceConfig, StrategyConfig strategyConfig, GlobalConfig config, PackageConfig packageConfig) throws NoSuchFieldException, IllegalAccessException {
        TemplateConfig templateConfig=new TemplateConfig();
//      templateConfig.disable(TemplateType.CONTROLLER,TemplateType.ENTITY,TemplateType.MAPPER,TemplateType.SERVICE,TemplateType.XML);
        templateConfig.disable(TemplateType.CONTROLLER,TemplateType.SERVICE);           //禁止生成controller+service类
        AutoGenerator autoGenerator= new AutoGenerator()
                .setGlobalConfig(config)
                .setDataSource(dataSourceConfig)
                .setStrategy(strategyConfig)
                .setPackageInfo(packageConfig)
                .setTemplateEngine(new FreemarkerTemplateEngine())
                .setTemplate(templateConfig);     //模板配置，禁用生成
        customPackagePath(packageConfig,autoGenerator);
        autoGenerator.execute();
    }


//方式一、关闭其他类生成
//    private TemplateConfig getTemplateConfig(){
//        TemplateConfig tc=new TemplateConfig();
//        tc.setController(null);     //关闭默认生成controller
//        tc.setService(null);
//        tc.setServiceImpl(null);
//        return tc;
//    }


    /**
     * 自定义包路径，文件生成路径，这边配置更灵活
     * 虽然也可以使用InjectionConfig设置FileOutConfig的方式设置路径
     * 这里直接使用Map方式注入ConfigBuilder配置对象更加直观
     * @param pc
     * @param mpg
     * @throws NoSuchFieldException
     * @throws IllegalAccessException
     */
    public void customPackagePath(PackageConfig pc,AutoGenerator mpg) throws NoSuchFieldException, IllegalAccessException {
        String proejctRootPath=env.getProperty("com.cool.faster.toGenerateProjectAbsPath");
        if(StringUtils.isEmpty(proejctRootPath)){
            proejctRootPath=System.getProperty("user.dir");
        }
        String javaPath=proejctRootPath+"/src/main/java/";
        /**
         * packageInfo配置controller、service、serviceImpl、entity、mapper等文件的包路径
         * 这里包路径可以根据实际情况灵活配置
         */
        Map<String,String> packageInfo = new HashMap<>();
//        packageInfo.put(ConstVal.CONTROLLER, env.getProperty("com.cool.faster.controllerPackage"));
//        packageInfo.put(ConstVal.SERVICE, env.getProperty("com.cool.faster.servicePackage"));
//        packageInfo.put(ConstVal.SERVICE_IMPL, env.getProperty("com.cool.faster.serviceImplPackage"));
        packageInfo.put(ConstVal.ENTITY, env.getProperty("com.cool.faster.modelPackage"));
        packageInfo.put(ConstVal.MAPPER,  env.getProperty("com.cool.faster.daoPackage"));

        /**
         * pathInfo配置controller、service、serviceImpl、entity、mapper、mapper.xml等文件的生成路径
         * srcPath也可以更具实际情况灵活配置
         * 后面部分的路径是和上面packageInfo包路径对应的源码文件夹路径
         * 这里你可以选择注释其中某些路径，可忽略生成该类型的文件，例如:注释掉下面pathInfo中Controller的路径，就不会生成Controller文件
         */
        Map pathInfo = new HashMap<>();
//        pathInfo.put(ConstVal.CONTROLLER_PATH, javaPath + packageInfo.get(ConstVal.CONTROLLER).replaceAll("\\.", StringPool.BACK_SLASH + File.separator));
//        pathInfo.put(ConstVal.SERVICE_PATH, javaPath + packageInfo.get(ConstVal.SERVICE).replaceAll("\\.", StringPool.BACK_SLASH + File.separator));
//        pathInfo.put(ConstVal.SERVICE_IMPL_PATH, javaPath + packageInfo.get(ConstVal.SERVICE_IMPL).replaceAll("\\.", StringPool.BACK_SLASH + File.separator));
        pathInfo.put(ConstVal.ENTITY_PATH, javaPath + packageInfo.get(ConstVal.ENTITY).replaceAll("\\.", StringPool.BACK_SLASH + File.separator));
        pathInfo.put(ConstVal.MAPPER_PATH, javaPath + packageInfo.get(ConstVal.MAPPER).replaceAll("\\.", StringPool.BACK_SLASH + File.separator));
        pathInfo.put(ConstVal.XML_PATH, proejctRootPath+"\\src\\main\\resources\\mapper\\");
        pc.setPathInfo(pathInfo);

        /**
         * 创建configBuilder对象，传入必要的参数
         * 将以上的定义的包路径packageInfo配置到赋值到configBuilder对象的packageInfo属性上
         * 因为packageInfo是私有成员变量，也没有提交提供公共的方法，所以使用反射注入
         * 为啥要这么干，看源码去吧
         */
        ConfigBuilder configBuilder = new ConfigBuilder(mpg.getPackageInfo(), mpg.getDataSource(), mpg.getStrategy(), mpg.getTemplate(), mpg.getGlobalConfig());
        Field packageInfoField = configBuilder.getClass().getDeclaredField("packageInfo");
        packageInfoField.setAccessible(true);
        packageInfoField.set(configBuilder,packageInfo);
    }


    /**
     * 生成所有的controller
     * @param classSet
     */
    private void generateController(Set<Class<?>> classSet){
        for (Class<?> calzz : classSet) {
            String entityName=calzz.getSimpleName();
            String entityInstancePrifex= StrUtil.toLowerCaseFirstOne(entityName);
            //文件名称
            String fileName=entityName+"Controller";
            String serviceFileName=entityName+"Service";
            //读取作者信息
            String author= env.getProperty("com.cool.author");

            //设置上下文需要替换的值
            Context context = new Context();
            //设置作者
            context.setVariable("author", author);
            //文件名称
            context.setVariable("fileName", fileName);
            context.setVariable("serviceFileName", serviceFileName);
            //设置引用的包信息
            context.setVariable("modelPackage",env.getProperty("com.cool.faster.modelPackage"));
            context.setVariable("controllerPackage",env.getProperty("com.cool.faster.controllerPackage"));
            context.setVariable("servicePackage",env.getProperty("com.cool.faster.servicePackage"));
            //类的名称
            context.setVariable("entityName", entityName);
            //类的实例对象
            context.setVariable("entityInstancePrifex", entityInstancePrifex);
            //创建时间
            context.setVariable("createDate",  new DateTime().toString("yyyy年MM月dd日 HH:mm:ss EE", Locale.CHINESE));

            //获取替换后的文本信息
            String text = templateEngine.process("controller", context);
            //工程路径
            try {
                String proejctRootPath=env.getProperty("com.cool.faster.toGenerateProjectAbsPath");
                if(StringUtils.isEmpty(proejctRootPath)){
                    proejctRootPath=System.getProperty("user.dir");
                }
                String javaPath=proejctRootPath+"/src/main/java/";
                //生成javaa文件
                String  packatePath=env.getProperty("com.cool.faster.modelParentPackagePath");
                //controller所在的路径
                String controllerPath=javaPath+ File.separator+packatePath+"/controller";
                //生成javaa文件
                File javaFile= FileUtil.generateJavaFile(new File(controllerPath),fileName);
                FileUtil.write(javaFile,text);
            } catch (Exception e) {
                e.printStackTrace();
            }

            System.out.println(text);
        }
    }


    /**
     * 生成所有的service
     * @param classSet
     */
    private void generateService(Set<Class<?>> classSet){
        for (Class<?> calzz : classSet) {
            String entityName=calzz.getSimpleName();
            String entityInstancePrifex= StrUtil.toLowerCaseFirstOne(entityName);
            //文件名称
            String fileName=entityName+"Service";
            //读取作者信息
            String author= env.getProperty("com.cool.author");

            //设置上下文需要替换的值
            Context context = new Context();
            //设置作者
            context.setVariable("author", author);
            //文件名称
            context.setVariable("fileName", fileName);
            //设置引用的包信息
            context.setVariable("modelPackage",env.getProperty("com.cool.faster.modelPackage"));
            context.setVariable("servicePackage",env.getProperty("com.cool.faster.servicePackage"));
            context.setVariable("serviceImplPackage",env.getProperty("com.cool.faster.serviceImplPackage"));
            //类的名称
            context.setVariable("entityName", entityName);

            //类的实例对象
            context.setVariable("entityInstancePrifex", entityInstancePrifex);
            //创建时间
            context.setVariable("createDate",  new DateTime().toString("yyyy年MM月dd日 HH:mm:ss EE", Locale.CHINESE));
            //获取替换后的文本信息
            String text = templateEngine.process("service", context);

            //工程路径
            try {
                String proejctRootPath=env.getProperty("com.cool.faster.toGenerateProjectAbsPath");
                if(StringUtils.isEmpty(proejctRootPath)){
                    proejctRootPath=System.getProperty("user.dir");
                }
                String javaPath=proejctRootPath+"/src/main/java/";
                //生成javaa文件
                String  packatePath=env.getProperty("com.cool.faster.modelParentPackagePath");
                String servicePath=javaPath+ File.separator+packatePath+"/service";
                //生成javaa文件
                File javaFile=FileUtil.generateJavaFile(new File(servicePath),fileName);
                FileUtil.write(javaFile,text);
            } catch (Exception e) {
                e.printStackTrace();
            }

            System.out.println(text);
        }
    }


    /**
     * 生成所有的serviceImpl
     */
    public void generateServiceImpl(Set<Class<?>> classSet){
        for (Class<?> calzz : classSet) {
            String entityName=calzz.getSimpleName();
            String entityInstancePrifex= StrUtil.toLowerCaseFirstOne(entityName);

            String servcieFileName=entityName+"Service";
            //文件名称
            String fileName=entityName+"ServiceImpl";

            //读取作者信息
            String author= env.getProperty("com.cool.author");

            //设置上下文需要替换的值
            Context context = new Context();
            //设置作者
            context.setVariable("author", author);
            //文件名称
            context.setVariable("fileName", fileName);
            //文件名称
            context.setVariable("servcieFileName", servcieFileName);
            //设置引用的包信息
            context.setVariable("modelPackage",env.getProperty("com.cool.faster.modelPackage"));
            context.setVariable("servicePackage",env.getProperty("com.cool.faster.servicePackage"));
            context.setVariable("serviceImplPackage",env.getProperty("com.cool.faster.serviceImplPackage"));
            context.setVariable("daoPackage",env.getProperty("com.cool.faster.daoPackage"));
            //类的名称
            context.setVariable("entityName", entityName);
            //类的实例对象
            context.setVariable("entityInstancePrifex", entityInstancePrifex);
            //创建时间
            context.setVariable("createDate",  new DateTime().toString("yyyy年MM月dd日 HH:mm:ss EE", Locale.CHINESE));
            //获取替换后的文本信息
            String text = templateEngine.process("serviceImpl", context);

            //工程路径
            try {
                String proejctRootPath=env.getProperty("com.cool.faster.toGenerateProjectAbsPath");
                if(StringUtils.isEmpty(proejctRootPath)){
                    proejctRootPath=System.getProperty("user.dir");
                }
                String javaPath=proejctRootPath+"/src/main/java/";
                //生成javaa文件
                String  packatePath=env.getProperty("com.cool.faster.modelParentPackagePath");
                String serviceImplPath=javaPath+ File.separator+packatePath+"/service/impl";
                File javaFile= FileUtil.generateJavaFile(new File(serviceImplPath),fileName);
                FileUtil.write(javaFile,text);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


//    /**
//     * 获取项目根路径
//     * @param
//     * @return
//     */
//    public  String getProjectPath(){
//        String proejctPath= env.getProperty("com.cool.faster.toGenerateProjectAbsPath");
//        if(StringUtils.isEmpty(proejctPath)){
//            return System.getProperty("user.dir");
//        }
//        return proejctPath;
//    }
//
//    /**
//     * 获取项目根路径
//     * @param
//     * @return
//     */
//    public  String getPackagePath(){
//        return env.getProperty("com.cool.faster.modelParentPackagePath");
//    }
//
//
//    /**
//     * 获取java Path
//     * @param
//     * @return
//     */
//    public  String getJavaPath(){
//        return getProjectPath()+"/src/main/java/";
//    }

}
